//
// Created by tianq on 25-5-3.
// Later we want one more type of shape as triangles and rectangles but we no longer squares and circles.
//

#include <cmath>
#include <iostream>
#include <memory>
#include <vector>
#include <SFML/Graphics.hpp>
using namespace std;

class Shape {
protected:
    sf::Vector2f position{0,0};
    float rotation = 0.0f;       // in degrees

public:
    virtual ~Shape() = default;

    void setPosition(const sf::Vector2f pos) { position = pos; }
    void setPosition(const float x, const float y) { position = {x, y}; }
    void setRotation(const float degrees) { rotation = degrees; }

    // unified render entry
    std::unique_ptr<sf::Shape> render() {
        auto shape = draw();      // draw implemented by subclasses
        applyTransform(shape);    // apply transform
        return shape;
    }

private:
    // transform performed by base class,
    // subclasses should not mess with it, hence private here
    void applyTransform(const std::unique_ptr<sf::Shape>& shape) const {
        shape->setPosition(position);
        shape->setRotation(sf::degrees(rotation));
    }

    virtual std::unique_ptr<sf::Shape> draw() = 0;
};

class Triangle final : public Shape {
    float a{}, b{}, c{};

public:
    // equilateral triangle
    explicit Triangle(const float side = 1.0f) {
        cout << "Triangle::Triangle(side=" << side << ")" << endl;
        this->a = b = c = side;
    }

    explicit Triangle(const float a, const float b, const float c) {
        cout << "Triangle::Triangle(a=" << a << ", b=" << b << ", c=" << c << ")" << endl;
        this->a = a;
        this->b = b;
        this->c = c;
    }

    std::unique_ptr<sf::Shape> draw() override {
        // calculate area using Heron's formula
        const float s = (a + b + c) / 2;
        const float area = sqrt(s * (s - a) * (s - b) * (s - c));
        cout << "Triangle::draw(), area=" << area << endl;

        // calculate position of the third point
        // holy shit why am I storing a triangle like this
        const float x = (a*a + b*b - c*c) / (2 * a);
        const float y = std::sqrt(b*b - x*x);

        auto t = std::make_unique<sf::ConvexShape>(3);
        t->setPoint(0, sf::Vector2f(0, 0));
        t->setPoint(1, sf::Vector2f(a, 0));
        t->setPoint(2, sf::Vector2f(x, y));
        return t;
    }
};

class Rectangle final : public Shape {
    float w{}, h{};

public:
    // square
    explicit Rectangle(const float side = 1.0f) {
        cout << "Rectangle::Rectangle(side=" << side << ")" << endl;
        this->w = h = side;
    }

    explicit Rectangle(const float w, const float h) {
        cout << "Rectangle::Rectangle(w=" << w << ", h=" << h << ")" << endl;
        this->w = w;
        this->h = h;
    }

    std::unique_ptr<sf::Shape> draw() override {
        cout << "Rectangle::draw(), area=" << w * h << endl;
        return std::make_unique<sf::RectangleShape>(sf::Vector2f(w, h));
    }
};


int main() {
    vector<unique_ptr<Shape>> shapes;

    shapes.push_back(make_unique<Triangle>(100.0f));

    auto t = make_unique<Triangle>(300.0f, 400.0f, 500.0f);
    t->setPosition(100,200);
    t->setRotation(-30);
    shapes.push_back(std::move(t));

    auto q = make_unique<Rectangle>(100.0f,200.0f);
    q->setPosition(600,200);
    q->setRotation(30);
    shapes.push_back(std::move(q));

    sf::RenderWindow window(sf::VideoMode({800, 600}), "SFML Draw v2");

    for (const unique_ptr<Shape>& s : shapes) {
        window.draw(*s->render());
    }

    window.display();

    while (const std::optional event = window.waitEvent())
    {
        if (!event->is<sf::Event::Closed>()) continue;
        window.close();
        return 0;
    }
}
